
const LINENUM = 9;
const LINENUM_RADOM = 10;
const rateConfig = [[.2, .6], [.3, .7], [.4, .8], [.5, .9]];//混淆强度
var RATE = [0, 0];

const { count } = require('console');
var fs = require('fs');
var path = require('path');

var filterFils = [];
var filterDirs = [];

var classExpString = "";
var traceName = "";
var funString = "";

var filtFunctionName = ["validateNow", "addEventListener", "removeEventListener", "once",
	"dispatchEventWith", "dispatchEvent", "getChildAt", "getChildIndex", "addChildAt", "addChild",
	"removeChild", "removeChildren", "hitTestPoint", "childrenCreated", "dataChanged", "partAdded", "globalToLocal",
	"forEach", "Array", "Math"];

var arguments = process.argv.splice(2);
classExpString = arguments[1];
traceName = arguments[1].toLowerCase() + getRadomStr(9, 1);
funString = ' = function(e){ location.search = "';
var strong = arguments[2]; //取值1-4
if (strong < 1 || strong > 4) strong = 3;
RATE = rateConfig[strong - 1];
if (arguments.length > 3) {
	var expellClass = arguments[3].split(","); //需要排除的类名不做混淆 ["aaa","bbb"];
}
skinFileUrl = ""; //settings.js
if (arguments.length > 4) {
	//skinFileUrl = arguments[4]; //皮肤json路径
	var skinFileList = {};
	var skinFileListNew = {};
	fileDisplay(arguments[4]);
}

// node changejs1.js /Users/zhixiang/Documents/proj/minigame-btxd/client_btxd_z/build/wechatgame/src/project.41ad2.js BaoShi 4 'SkillConfirmView,SkillSelectView,SkillTipsView,ToolManager,PersonCreateView,ReliveView,ResultNewView_IOS,RadiusDamage,MoreGoldView_IOS,MenuView_IOS,InviteAndService_IOS,GroupView_IOS,EventView,FightUI,BaseSkill,Bullet,DropPreview,CurrencyView,UIRole,SelectAlert,GetDiamondPanel_IOS,GetRewardPanel_IOS,MovingEntity,Monster,MainRole,Gun,GoldEffect,GameWorld,EquipDrop,GetDiamondPanel_IOS,ExchangePower,ExchangeDiamond,Session,State,Watcher,Time,Const,Agent,lzstring,ModuleLauncher,Global,Launcher,Array,Bright,ShaderHelper,CustomMaterial,ShaderHook,Math,LoadingView' '/Users/zhixiang/Documents/proj/minigame-btxd/client_btxd_z/build/wechatgame/res/import'

var jsurl = arguments[0];
//md5Code(jsurl);
setTimeout(() => { md5Code(jsurl); }, 2000)

function fileDisplay(filePath) {
	//根据文件路径读取文件，返回文件列表
	fs.readdir(filePath, function (err, files) {
		if (err) {
			console.warn(err)
		} else {
			//遍历读取到的文件列表
			files.forEach(function (filename) {
				//获取当前文件的绝对路径
				var filedir = path.join(filePath, filename);
				//根据文件路径获取文件信息，返回一个fs.Stats对象
				fs.stat(filedir, function (eror, stats) {
					if (eror) {
						console.warn('获取文件stats失败');
					} else {
						var isFile = stats.isFile();//是文件
						var isDir = stats.isDirectory();//是文件夹
						if (isFile) {
							//console.log(filename,filedir);
							let onlyName = filename.split(".")[0];
							if (filename.indexOf(".json") != -1 && filterFils.indexOf(filename) == -1) {
								//console.log(filedir);
								skinFileList[filedir] = skinFileListNew[filedir] = fs.readFileSync(filedir, { encoding: "utf8" });
							}
						}
						if (isDir) {
							if (!filterDirs || filterDirs.indexOf(filename) == -1) {
								fileDisplay(filedir);//递归，如果是文件夹，就继续遍历该文件夹下面的文件
							}
						}
					}
				})
			});
		}
	});
}

function md5Code(phppath) {

	console.log("执行文件：" + phppath);
	let codeContent = fs.readFileSync(phppath, { encoding: "utf8" });
	//console.log(codeContent);

	/** 格式化代码 ******************/
	var js_parse = require("./js_parse.js");
	codeContent = js_parse.js_beautify(codeContent, 4, ' ');


	//====第1步：修改私有方法名==================================================================
	//【修改代码】
	let arr = codeContent.match(/this\.\s*(\w+)\(/g); //寻找所有 this.xxxx(的方法名
	arr = [...new Set(arr)];//去除重复的字符串
	arr = arr.map(function (value) {
		return value.replace("this.", "").replace("(", "");
	});
	arr = arr.filter(function (value) {
		return filtFunctionName.indexOf(value) < 0;
	});

	var changeFunction = false; //是否修改私有方法名称，默认关闭
	if (arr && changeFunction) {
		var funExp, result, newName, regExp;
		for (var i = 0; i < arr.length; i++) {
			//需要判断方法名是否有被多次非this调用，如果是，说明是public (注意要区分大小写)
			regExp = new RegExp("\\w*?\\." + arr[i] + "\\(", 'g'); //xxxxx.funname(
			if (codeContent.match(regExp).length > 1) continue;

			regExp = new RegExp("\\w*?\\." + arr[i] + "\\,", 'g'); //xxxxx.funname,
			if (codeContent.match(regExp)) continue;

			regExp = new RegExp("\\w*?\\." + arr[i] + "\\)", 'g'); //xxxxx.funname)
			if (codeContent.match(regExp)) continue;

			funExp = new RegExp("\\.prototype\\." + arr[i] + " = function\\(", 'g'); //e.prototype.funname = function(
			result = codeContent.match(funExp);
			//console.log(funExp,result);

			if (result) {
				if (result.length != 1) continue; //出现多次同名方法，说明有可能出现继承关系

				//newName = arr[i] + "_" + Math.floor(Math.random() * 9999);
				newname = encryptName(arr[i], classExpString);
				codeContent = codeContent.replace(funExp, ".prototype." + newName + " = function(");
			}
			else {//判断静态方法
				funExp = new RegExp("\\." + arr[i] + " = function\\(", 'g'); //e.funname = function(
				result = codeContent.match(funExp);
				//console.log(funExp,result);
				if (!result) continue;
				if (result.length != 1) continue; //出现多次同名方法，说明有可能出现继承关系

				//newName = arr[i] + "_" + Math.floor(Math.random() * 9999);
				newname = encryptName(arr[i], classExpString);
				codeContent = codeContent.replace(funExp, "." + newName + " = function(");
			}

			regExp = new RegExp("this\\." + arr[i] + "\\(", 'g'); //this.funname(
			codeContent = codeContent.replace(regExp, "this." + newName + "(");
		}
	}


	//====第2步：增加干扰代码 插入一行无意义的console代码 ==================================================================
	let arr2 = codeContent.split("\n");
	for (var i = LINENUM; i < arr2.length - 1; i++) {
		if (!arr2[i]) continue;
		let formatStr = Trim(arr2[i], "g");
		if (formatStr == "" || formatStr == "{") continue;
		arr2[i] = String(arr2[i]);

		if (arr2[i].indexOf("else") != -1) continue;
		if (arr2[i].indexOf("else if") != -1) continue;
		if (arr2[i].indexOf("//") != -1) continue;
		if (arr2[i].indexOf("catch(") != -1) continue;
		if (arr2[i].indexOf("function") == -1 && arr2[i].indexOf(":") != -1) continue; //object里面的key value

		if (arr2[i].indexOf("function(") != -1) continue;
		if (arr2[i].indexOf("Array") != -1) continue;
		if (arr2[i].indexOf("}(") != -1) continue; //({
		if (arr2[i].indexOf("({\"") != -1) continue; //
		if (arr2[i].indexOf("\":") != -1) continue; //({
		if (arr2[i].indexOf("\" :") != -1) continue; //({
		if (arr2[i].indexOf("Material") != -1) continue;
		if (arr2[i].indexOf("case") != -1) continue;
		if (arr2[i].indexOf("= this && this.__") != -1) continue;

		//if(!arr2[i-1]) continue;
		let formatStr2 = Trim(arr2[i - 1], "g");
		//arr2[i-1] = String(arr2[i-1]);
		if (arr2[i - 1].indexOf("return") != -1) continue;

		if (formatStr2 == "}," || formatStr2.substr(-2) == "},") continue; //一个方法 有function类型的参数，会分行显示参数，需要过滤
		else if (formatStr2 == "}()," || formatStr2.substr(-5) == "} (),") continue; //一个方法 复制给一个类属性，需要过滤

		if (arr2[i].indexOf("function") != -1 && arr2[i - 1].indexOf(",") != -1) continue; //object里面的key value
		if (arr2[i].indexOf("}") != -1 && arr2[i - 1].indexOf(":") != -1) continue; //object里面的结尾
		if (arr2[i].indexOf("},") != -1 && arr2[i + 1].indexOf("\"") != -1) continue; //object里面的结尾
		if (formatStr2.substr(-1) != ";" && formatStr2.substr(-1) != ",") continue;
		if (true) {//需要过滤那种 var 定义变量的情况
			let aa = i, goContinue = false;
			let currentLeftSpace = getLeftSpace(arr2[i]);
			let leftSpace;
			while (aa >= 0) {
				leftSpace = getLeftSpace(arr2[aa - 1]);
				if (arr2[aa - 1].substr(-1) == ";" && currentLeftSpace == leftSpace)
					break;
				if (arr2[aa - 1].indexOf("set: function(") >= 0 || arr2[aa - 1].indexOf("get: function(") >= 0 || arr2[aa - 1].indexOf("}(")) {
					goContinue = true;
					break;
				}
				if (arr2[aa - 1].indexOf("function(") >= 0 && currentLeftSpace != leftSpace)
					break;
				if (arr2[aa - 1].indexOf("var ") >= 0 || arr2[aa - 1].indexOf("let ") >= 0 || arr2[aa - 1].indexOf("const ") >= 0) {
					goContinue = true;
					break;
				}
				aa--;
			}
			if (goContinue) continue;
		}

		let lastIsFenStr = formatStr2.substr(-1) == ";";

		let randomNum = Math.random();
		if (randomNum < 0.5) {
			var str = getAddSpace(arr2[i]) + traceName + "(\"" + getRadomStr() + "\")" + (lastIsFenStr ? ";" : ",");
		} else {
			var str = getAddSpace(arr2[i]) + traceName + "(" + Math.ceil(randomNum * 199) + ")" + (lastIsFenStr ? ";" : ",");
		}
		arr2.splice(i, 0, str);
		i = i + LINENUM + (Math.round(randomNum * LINENUM_RADOM) * (randomNum < 0.5 ? 1 : -1));//随机行数添加
	}
	//添加打印方法
	arr2.unshift("function " + traceName + "(t) {    for (var e = [], i = 1; i < arguments.length; i++) e[i - 1] = arguments[i] }")


	//====第3步：增加干扰代码 插入一个不会被引用的方法 ==================================================================
	let rate = RATE[0] || 0.3;
	for (var i = LINENUM; i < arr2.length - 1; i++) {

		let formatStr = Trim(arr2[i], "g"); //当前行
		//arr2[i] = String(arr2[i]);
		let formatStr2 = Trim(arr2[i - 1], "g"); //上一行
		//arr2[i-1] = String(arr2[i-1]);
		let formatStr3 = Trim(arr2[i + 1], "g"); //下一行
		//arr2[i+1] = String(arr2[i+1]);

		let canInsert = false;
		//【修改代码】
		///let isFun = (arr2[i].indexOf("           ") !=-1 ) && arr2[i].indexOf("(") !=-1 && arr2[i].indexOf(") {") !=-1; //用这种方法来判断是类方法
		let isFun = (arr2[i].indexOf(".prototype.") != -1) && arr2[i].indexOf("= function(") != -1;
		var str = '';
		if (isFun && formatStr2 == "},") {
			canInsert = Math.random() < rate;
			var classStr = formatStr.split(".")[0];
			if (canInsert && classStr != 'cc') {
				str = getAddSpace(arr2[i]) + classStr + '.prototype.set' + getRadomStr(4, 1) + funString + getRadomStr() + '"; },';
				// console.log(str)
				arr2.splice(i, 0, str);
			}
		}
		else {
			///isFun = (arr2[i+1].indexOf("           ") !=-1 ) && formatStr3.indexOf("(") !=-1 && arr2[i].indexOf(") {") !=-1;
			isFun = (formatStr3.indexOf(".prototype.") != -1) && formatStr3.indexOf("= function(") != -1;
			if (formatStr == "}," && isFun) {
				canInsert = Math.random() < rate;
				if (canInsert) {
					var classStr = formatStr3.split(".")[0];
					str = getAddSpace(arr2[i + 1]) + classStr + '.prototype.set' + getRadomStr(4, 1) + funString + getRadomStr() + '"; },';
					console.log('str====')
					arr2.splice(i, 0, str);
				}
			}
		}
	}

	codeContent = arr2.join("\n");


	//====修改类名（需要一起改皮肤）==================================================================
	//特别注意，类名是全局替换，不要使用类名作为 字段名
	//修改类名，读取最后一行的类名，进行修改
	let lastLine = arr2[arr2.length - 1].match(/\[.*\]/g); //["ball", "scale", "vv", "welcome"]);
	if (lastLine && lastLine.length > 0 && true) { //有开关
		var classArray = JSON.parse(lastLine[0]);
		console.log(classArray.length)

		let skinContent = "";
		if (skinFileUrl && fs.existsSync(skinFileUrl))
			skinContent = fs.readFileSync(skinFileUrl, { encoding: "utf8" });
		let skinContent2 = skinContent;

		let ccount = 0

		for (var i = 0; i < classArray.length; i++) {
			if (Math.random() > 2) //不要改全部的类，只改80%
				continue;
			let name = classArray[i];
			if (expellClass && expellClass.indexOf(name) > -1)
				continue;
			if (name == "Main" || name == "_get") //Main是入口函数，不改名
				continue;
			if (name.toLocaleLowerCase() == name) continue; //纯小写的，有可能是 ui变量名
			if (name.indexOf() == '_') continue; //纯小写的，有可能是 ui变量名 View
			if ((name + '').indexOf("view") > -1) continue; //纯小写的，有可能是 ui变量名 
			if ((name + '').indexOf("View") > -1) continue; //纯小写的，有可能是 ui变量名 
			if (global[name]) continue;
			// if(ccount>280) break; //逐个类检查
			// console.log("name:",name);

			regExp = new RegExp("\\b" + name + "\\b", 'g'); //匹配整个单词
			let newname = encryptName(name, classExpString);
			codeContent = codeContent.replace(regExp, newname);
			ccount++;
			console.log("newname", newname)

			//修改皮肤json文件里面的类名
			skinContent2 = skinContent2.replace(regExp, newname);

			if (skinFileListNew) {
				for (var jj in skinFileListNew) {
					//console.log(jj);
					skinFileListNew[jj] = skinFileListNew[jj].replace(regExp, newname);
				}
			}
		}
		if (skinContent && skinContent != skinContent2) {
			fs.writeFileSync(skinFileUrl, skinContent2, { encoding: "utf8" });
			console.log("json-----" + skinFileUrl);
		}
		for (var jj in skinFileListNew) {
			if (skinFileList[jj] != skinFileListNew[jj]) {
				fs.writeFileSync(jj, skinFileListNew[jj], { encoding: "utf8" });
				console.log("json-----" + jj);
			}
		}
	}

	//console.log(codeContent);

	/** 压缩代码 ******************/
	// codeContent = js_parse.jsmin("", codeContent, 2);

	fs.writeFileSync(phppath, codeContent, { encoding: "utf8" });
	console.log("执行完成！--" + phppath);
}

function encryptName(name, key) {
	var newname = name.split("");
	for (var i = 0; i < name.length; i++) {
		let code = name.charCodeAt(i) % 20;
		if (i < key.length) {
			let code2 = key.charCodeAt(i);
			let word = key.charAt(i);
			if ((code2 + code <= 122 && code2 + code >= 97) || (code2 + code <= 90)) {
				let newCode = code2 + code;
				if (newCode > 122) {
					newCode = newCode - 26;
				}
				word = String.fromCharCode(newCode);

				// 引入随机因素
				let randomCode = Math.floor(Math.random() * 26); // 生成0到25之间的随机数
				let randomWord = String.fromCharCode(97 + randomCode); // 将随机数转换为对应的小写字母
				word += randomWord;
			} else {
				word = '';
			}
			newname[i] = newname[i] + word;
		}
	}
	return newname.join("").replace(/[^A-Za-z]/g, '');
}

function jiaMiKey(name, key) {
	//var name = "abcd";
	//var key = 'Proo';
	var newname = name.split("");
	for (var i = 0; i < name.length; i++) {
		let code = name.charCodeAt(i) % 20;
		if (i < key.length) {
			let code2 = key.charCodeAt(i);
			let word = key.charAt(i);
			if ((code2 + code <= 122 && code2 + code >= 97) || (code2 + code <= 90)) {
				word = String.fromCharCode(code2 + code);
			}
			//判断插入前面还是后面
			//if(code%2 == 0) //往前插
			//	newname[i] = word + newname[i];
			//else
			newname[i] = newname[i] + word;
		}
	}
	return newname.join("");
}

function getLeftSpace(str) {
	if (!str) return 0;
	let num = str.length - lTrim(str).length;
	return num;
}

function getAddSpace(str) {
	if (!str) return "";
	let num = str.length - lTrim(str).length;
	let formatStr = Trim(str, "g");
	if (formatStr == "}")
		num += 4;
	let space = "";
	while (num > 0) {
		space += " ";
		num--;
	}
	return space;
}
function getRadomStr(len, type) {
	len = len || Math.floor(Math.random() * 32) + 1;
	var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';    /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
	if (type == 1) $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz'; //非数字
	var maxPos = $chars.length;
	var pwd = '';
	for (var i = 0; i < len; i++) {
		pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
	}
	return pwd;
}
function Trim(str, is_global) {
	//console.log("==", str)
	str = String(str);
	var result;
	result = str.replace(/(^\s+)|(\s+$)/g, "");
	if (is_global && is_global.toLowerCase() == "g") {
		result = result.replace(/\s/g, "");
	}
	return result;
}
function lTrim(str) {
	str = String(str);
	var result = str.replace(/(^\s+)/g, "");
	return result;
}
